package io.kiki.sba.registry.server.clientInterface.resource;


import io.kiki.sba.registry.api.exchange.NodeExchanger;
import io.kiki.sba.registry.api.exchange.message.SimpleRequest;
import io.kiki.sba.registry.common.model.ClientToClientInterfaceServerNodeId;
import io.kiki.sba.registry.common.model.CollectionSdks;
import io.kiki.sba.registry.common.model.GenericResponse;
import io.kiki.sba.registry.common.model.Response;
import io.kiki.sba.registry.common.model.sessionserver.ClientManagerQueryRequest;
import io.kiki.sba.registry.common.model.sessionserver.ClientManagerResp;
import io.kiki.sba.registry.common.model.sessionserver.ClientOffRequest;
import io.kiki.sba.registry.common.model.sessionserver.ClientOnRequest;
import io.kiki.sba.registry.common.model.store.Url;
import io.kiki.sba.registry.server.clientInterface.bootstrap.Config;
import io.kiki.sba.registry.server.clientInterface.bootstrap.ExecutorManager;
import io.kiki.sba.registry.server.clientInterface.connections.ConnectionsService;
import io.kiki.sba.registry.server.clientInterface.mapper.ConnectionMapper;
import io.kiki.sba.registry.server.clientInterface.providedata.FetchClientOffAddressService;
import io.kiki.sba.registry.server.clientInterface.registry.ClientInterfaceRegistry;
import io.kiki.sba.registry.server.shared.env.ServerEnv;
import io.kiki.sba.registry.server.shared.meta.MetaServerService;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import javax.annotation.Resource;
import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

/**
 * The type Clients open resource.
 */
@Path("api/clientManager")
@Produces(MediaType.APPLICATION_JSON)
public class ClientManagerResource {

    private static final Logger logger = LoggerFactory.getLogger(ClientManagerResource.class);
    @Autowired
    protected ExecutorManager executorManager;
    @Autowired
    private ClientInterfaceRegistry clientInterfaceRegistry;
    @Autowired
    private Config config;
    @Autowired
    private MetaServerService metaServerService;
    @Autowired
    private ConnectionsService connectionsService;
    @Autowired
    private ConnectionMapper connectionMapper;
    @Autowired
    private NodeExchanger sessionConsoleExchanger;
    @Resource
    private FetchClientOffAddressService fetchClientOffAddressService;

    /**
     * Client off
     */
    @POST
    @Path("/clientOff")
    public Response clientOff(@FormParam("ips") String ips) {
        if (StringUtils.isEmpty(ips)) {
            return Response.buildFailedResponse("ips is empty");
        }
        final Set<String> ipSet = CollectionSdks.toIpSet(ips);
        List<ClientToClientInterfaceServerNodeId> conIds = connectionsService.getIpConnects(ipSet);
        clientInterfaceRegistry.clientOff(conIds);
        logger.info("clientOff ips={}, conIds={}", ips, conIds);
        return Response.buildSuccessResponse();
    }


    @POST
    @Path("/clientOpen")
    public Response clientOn(@FormParam("ips") String ips) {
        if (StringUtils.isEmpty(ips)) {
            return Response.buildFailedResponse("ips is empty");
        }
        final List<String> ipList = CollectionSdks.toIpList(ips);
        List<String> conIds = connectionsService.closeIpConnects(ipList);
        logger.info("clientOn ips={}, conIds={}", ips, conIds);

        return Response.buildSuccessResponse();
    }


    @POST
    @Path("/zone/clientOff")
    public Response clientOffInZone(@FormParam("ips") String ips) {
        if (StringUtils.isEmpty(ips)) {
            return Response.buildFailedResponse("ips is empty");
        }
        Response resp = clientOff(ips);
        if (!resp.isSuccess()) {
            return resp;
        }

        final List<String> ipList = CollectionSdks.toIpList(ips);
        List<Url> servers = getOtherConsoleServersCurrentZone();
        logger.info("clientOffInZone, others={}", servers);
        if (servers.size() > 0) {
            Map<Url, Response> map = Sdks.concurrentSdkSend(executorManager.getZoneSdkExecutor(), servers, (Url url) -> {
                final ClientOffRequest req = new ClientOffRequest(ipList);
                return (Response) sessionConsoleExchanger.request(new SimpleRequest(req, url)).getResult();
            }, 3000);
            return Sdks.getFailedResponseIfAbsent(map.values());
        }
        return Response.buildSuccessResponse();
    }


    @POST
    @Path("/zone/clientOpen")
    public Response clientOnInZone(@FormParam("ips") String ips) {
        if (StringUtils.isEmpty(ips)) {
            return Response.buildFailedResponse("ips is empty");
        }
        Response resp = clientOn(ips);
        if (!resp.isSuccess()) {
            return resp;
        }
        final List<String> ipList = CollectionSdks.toIpList(ips);
        List<Url> servers = getOtherConsoleServersCurrentZone();
        logger.info("clientOnInZone, others={}", servers);
        if (servers.size() > 0) {
            Map<Url, Response> map = Sdks.concurrentSdkSend(executorManager.getZoneSdkExecutor(), servers, (Url url) -> {
                final ClientOnRequest req = new ClientOnRequest(ipList);
                return (Response) sessionConsoleExchanger.request(new SimpleRequest(req, url)).getResult();
            }, 3000);
            return Sdks.getFailedResponseIfAbsent(map.values());
        }
        return Response.buildSuccessResponse();
    }


    @POST
    @Path("/zone/queryClientOff")
    public GenericResponse<Map<String, ClientManagerResp>> queryClientOff() {
        Set<String> clientOffAddress = fetchClientOffAddressService.getClientOffAddress();
        List<Url> servers = getOtherConsoleServersCurrentZone();

        Map<String, ClientManagerResp> resp = new HashMap<>(servers.size() + 1);

        resp.put(ServerEnv.IP, new ClientManagerResp(true, clientOffAddress));
        if (servers.size() > 0) {
            Map<Url, Response> map = Sdks.concurrentSdkSend(executorManager.getZoneSdkExecutor(), servers, (Url url) -> {
                final ClientManagerQueryRequest req = new ClientManagerQueryRequest();
                return (Response) sessionConsoleExchanger.request(new SimpleRequest(req, url)).getResult();
            }, 3000);

            for (Entry<Url, Response> entry : map.entrySet()) {
                if (entry.getValue() instanceof GenericResponse) {
                    GenericResponse response = (GenericResponse) entry.getValue();
                    if (response.isSuccess()) {
                        resp.put(entry.getKey().getIp(), (ClientManagerResp) response.getData());
                        continue;
                    }
                }
                logger.error("url={} queryClientOff fail, msg:{}.", entry.getKey().getIp(), entry.getValue());
                resp.put(entry.getKey().getIp(), new ClientManagerResp(false));
            }
        }
        return new GenericResponse().fillSucceed(resp);
    }

    @GET
    @Path("/connectionMapper.json")
    public Map<String, String> connectionMapper() {
        return connectionMapper.get();
    }

    public List<Url> getOtherConsoleServersCurrentZone() {
        return Sdks.getOtherConsoleServers(null, config, metaServerService);
    }
}
